package com.aloha.springframework.aop.framework;

import com.aloha.springframework.aop.ProxyMethodInvocation;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.List;

/**
 * @author DaiZhiHeng
 * @description 使用反射时执行代理方法的类
 * @date 2023/7/7 9:43
 */
public class ReflectiveMethodInvocation implements ProxyMethodInvocation {

    /**
     * 代理对象
     */
    protected final Object proxy;

    /**
     * 被代理的目标对象
     */
    protected final Object target;

    /**
     * 目标对象的类型
     */
    protected final Class<?> targetClass;

    /**
     * 需要被增强的具体方法
     */
    protected final Method method;

    /**
     * 被增强方法的参数
     */
    protected Object[] arguments;

    /**
     * 目标类所有的增强
     */
    protected final List<Advice> interceptorsAndDynamicMethodMatchers;

    private int currentInterceptorIndex = -1;

    public ReflectiveMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, Class<?> targetClass, List<Advice> chain) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.arguments = arguments;
        this.targetClass = targetClass;
        this.interceptorsAndDynamicMethodMatchers = chain;
    }

    /**
     * 执行代理对象的方法
     */
    @Override
    public Object proceed() throws Throwable {
        // 初始currentInterceptorIndex为-1，每调用一次proceed就把currentInterceptorIndex+1
        if (currentInterceptorIndex == interceptorsAndDynamicMethodMatchers.size() - 1) {
            // 当调用次数 = 拦截器个数时
            // 触发当前method方法
            return method.invoke(target, arguments);
        }

        MethodInterceptor methodInterceptor =
                ((MethodInterceptor) interceptorsAndDynamicMethodMatchers.get(++currentInterceptorIndex));
        // 调用Advice增强的方法, 需要传入JointPoint连接点实现类
        return methodInterceptor.invoke(this);
    }

    @Override
    public Object getThis() {
        return target;
    }

    @Override
    public AccessibleObject getStaticPart() {
        return method;
    }

    @Override
    public Method getMethod() {
        return method;
    }

    @Override
    public void setArguments(Object[] arguments) {
        this.arguments = arguments;
    }

    @Override
    public Object[] getArguments() {
        return this.arguments;
    }

    @Override
    public Object getProxy() {
        return this.proxy;
    }
}
